package org.hbhk.hxl.gateway.route;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

/**
 * HTTP工具类.
 */
@SuppressWarnings("all")
public final class HttpClientUtil {

	/**
	 * 最大连接数
	 */
	private static final int MAX_TOTAL_CONNECTIONS = 2000;
	/**
	 * 获取连接的最大等待时间
	 */
	private static final int WAIT_TIMEOUT = 60 * 1000;
	/**
	 * 每个路由最大连接数
	 */
	private static final int MAX_ROUTE_CONNECTIONS = 30;
	/**
	 * 连接超时时间
	 */
	private static final int CONNECT_TIMEOUT = 5 * 1000;

	/**
	 * 读取超时时间
	 */
	@SuppressWarnings("unused")
	private static final int READ_TIMEOUT = 5 * 1000;

	/**
	 * VALIDATE_AFTER_INACTIVITY
	 */
	private static final int VALIDATE_AFTER_INACTIVITY = 2000;

	/**
	 * 缺省编码格式
	 */
	private static final String DEFAULT_CHARSET = "UTF-8";

	/**
	 * 锁
	 */
	private static final Object SYNC_LOCK = new Object();

	/**
	 * httpClient实例
	 */
	private static final AtomicReference<CloseableHttpClient> CLIENT = new AtomicReference<>();

	private HttpClientUtil() {
	}

	/**
	 * 设置Header
	 */
	@SuppressWarnings("all")
	private static void setHeader(final Map<String, String> headers, final HttpRequestBase httpRequestBase) {
		if (headers != null && headers.size() > 0) {
			headers.forEach((k, v) -> {
				httpRequestBase.setHeader(k, v);
			});
		}
	}

	/**
	 * 获取HttpClient对象
	 *
	 * @return CloseableHttpClient实例
	 */
	public static CloseableHttpClient getHttpClient() {
		if (CLIENT.get() == null) {
			synchronized (SYNC_LOCK) {
				if (CLIENT.get() == null) {
					CLIENT.set(createHttpClient());
				}
			}
		}
		return CLIENT.get();
	}

	/**
	 * 创建HttpClient对象
	 *
	 * @return CloseableHttpClient httpclient
	 */
	@SuppressWarnings("all")
	public static CloseableHttpClient createHttpClient() {
		final RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(READ_TIMEOUT)
				.setConnectTimeout(CONNECT_TIMEOUT).setConnectionRequestTimeout(WAIT_TIMEOUT).build();

		final Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
				.register("http", PlainConnectionSocketFactory.getSocketFactory())
				.register("https", SSLConnectionSocketFactory.getSocketFactory()).build();
		final PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
		// 将最大连接数增加
		cm.setMaxTotal(MAX_TOTAL_CONNECTIONS);
		// 将每个路由基础的连接增加
		cm.setDefaultMaxPerRoute(MAX_ROUTE_CONNECTIONS);
		cm.setValidateAfterInactivity(VALIDATE_AFTER_INACTIVITY);

		final CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm)
				// .disableCookieManagement()
				.setConnectionManagerShared(false)// 连接池不是共享模式，这个共享是指与其它httpClient是否共享
				.evictIdleConnections(10, TimeUnit.SECONDS)// 定期回收空闲连接, 解决close_wait问题
				.evictExpiredConnections()// 回收过期连接
				.setConnectionTimeToLive(60, TimeUnit.SECONDS)// 连接存活时间，如果不设置，则根据长连接信息决定
				.setDefaultRequestConfig(requestConfig).useSystemProperties()// 设置默认的请求参数
				.setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE)// 连接重用策略，即是否能keepAlive
				.setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)// 长连接配置，即获取长连接生产多长时间
				.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))// 设置重试次数，默认为3次；
				.build();

		return httpClient;
	}

	/**
	 * 设置post请求参数
	 *
	 * @param httpost the httpost
	 * @param params  the params
	 */
	private static void setPostParams(final HttpPost httpost, final Map<String, Object> params)
			throws UnsupportedEncodingException {
		if (params == null || params.size() == 0) {
			return;
		}
		List<NameValuePair> nvps = new ArrayList<NameValuePair>();
		Set<String> keySet = params.keySet();
		for (String key : keySet) {
			nvps.add(new BasicNameValuePair(key, params.get(key).toString()));
		}
		httpost.setEntity(new UrlEncodedFormEntity(nvps, DEFAULT_CHARSET));
	}

	/**
	 * 发送post请求
	 *
	 * @param url         the url
	 * @param httppost    the httppost
	 * @param params      the params
	 * @param reqeustBody the reqeustBody
	 * @return String
	 * @throws Exception e
	 */
	private static String doPost(final String url, final HttpPost httppost, final String reqeustBody,
			final Map<String, Object> params) throws Exception {
		setPostParams(httppost, params);
		CloseableHttpResponse response = null;
		try {
			if (!(reqeustBody == null || reqeustBody.equals(""))) {
				StringEntity sn = new StringEntity(reqeustBody, "utf-8");
				// sn.setContentType("application/x-www-form-urlencoded");
				sn.setContentType("application/json");
				// sn.setContentType("application/xml;charset=utf-8");
				httppost.setEntity(sn);
			}
			response = getHttpClient().execute(httppost, HttpClientContext.create());
			HttpEntity entity = response.getEntity();
			return EntityUtils.toString(entity, DEFAULT_CHARSET);
		} finally {
			if (response != null) {
				response.close();
			}
			httppost.releaseConnection();
		}
	}

	/**
	 * 普通的post请求，获取内容
	 *
	 * @param url         the url
	 * @param params      the params
	 * @param reqeustBody the reqeustBody
	 * @return string
	 * @throws Exception e
	 */
	public static String post(final String url, final String reqeustBody, final Map<String, Object> params)
			throws Exception {
		return doPost(url, new HttpPost(url), reqeustBody, params);
	}

	/**
	 * 模拟的post请求，获取内容
	 *
	 * @param url         the url
	 * @param params      the params
	 * @param headers     the headers
	 * @param reqeustBody the reqeustBody
	 * @return string
	 * @throws Exception e
	 */
	public static String browserPost(final String url, final Map<String, String> headers, final String reqeustBody,
			final Map<String, Object> params) throws Exception {
		HttpPost httppost = new HttpPost(url);
		setHeader(headers, httppost);
		return doPost(url, httppost, reqeustBody, params);
	}

	/**
	 * 发送get请求
	 *
	 * @param url     the url
	 * @param httpget the httpget
	 * @return String
	 * @throws IOException e
	 */
	private static String doGet(final String url, final HttpGet httpget) throws IOException {
		CloseableHttpResponse response = null;
		try {
			response = getHttpClient().execute(httpget, HttpClientContext.create());
			HttpEntity entity = response.getEntity();
			return EntityUtils.toString(entity, DEFAULT_CHARSET);
		} finally {
			if (response != null) {
				response.close();
			}
			httpget.releaseConnection();
		}
	}

	/**
	 * GET请求URL获取内容
	 *
	 * @param url the url
	 * @return string
	 * @throws IOException e
	 */
	public static String get(final String url) throws IOException {
		return doGet(url, new HttpGet(url));
	}

	/**
	 * 模拟浏览器GET请求URL获取内容
	 *
	 * @param url     the url
	 * @param headers the headers
	 * @return string
	 * @throws IOException e
	 */
	public static String browserGet(final String url, final Map<String, String> headers) throws IOException {
		HttpGet httpget = new HttpGet(url);
		setHeader(headers, httpget);
		return doGet(url, httpget);
	}

	/**
	 * Decode Unicode
	 *
	 * @param theString the theString
	 * @return string
	 * @throws IOException e
	 */
	@SuppressWarnings("all")
	public static String decodeUnicode(final String theString) {
		char aChar;
		int len = theString.length();
		StringBuffer outBuffer = new StringBuffer(len);
		for (int x = 0; x < len;) {
			aChar = theString.charAt(x++);
			if (aChar == '\\') {
				aChar = theString.charAt(x++);
				if (aChar == 'u') {
					// Read the xxxx
					int value = 0;
					for (int i = 0; i < 4; i++) {
						aChar = theString.charAt(x++);
						switch (aChar) {
						case '0':
						case '1':
						case '2':
						case '3':
						case '4':
						case '5':
						case '6':
						case '7':
						case '8':
						case '9':
							value = (value << 4) + aChar - '0';
							break;
						case 'a':
						case 'b':
						case 'c':
						case 'd':
						case 'e':
						case 'f':
							value = (value << 4) + 10 + aChar - 'a';
							break;
						case 'A':
						case 'B':
						case 'C':
						case 'D':
						case 'E':
						case 'F':
							value = (value << 4) + 10 + aChar - 'A';
							break;
						default:
							throw new IllegalArgumentException("Malformed   \\uxxxx   encoding.");
						}

					}
					outBuffer.append((char) value);
				} else {
					if (aChar == 't') {
						aChar = '\t';
					} else if (aChar == 'r') {
						aChar = '\r';
					} else if (aChar == 'n') {
						aChar = '\n';
					} else if (aChar == 'f') {
						aChar = '\f';
					}
					outBuffer.append(aChar);
				}
			} else {
				outBuffer.append(aChar);
			}
		}
		return outBuffer.toString();
	}
}
